(* grapheng.ml - Graphical Engine
 * Licence: LGPLv3
 * Authors: Franck "kushou" Michea < michea_f@epita.fr >
 *)

let ( |> ) x f = f x

(* Global Vars *)

let started = ref false

let xold = ref 0
let yold = ref 0

let b_down = ref false

let anglex = ref 0
let angley = ref 0

type modes = Wire | Point | Fill
let mode = ref Fill

let fullscreen = ref false

let (objs:Point.point Triangle.triangle list list ref) = ref []
let heights = ref []

let scale = 2.0

let fit (x, y, z) =
  let foi = float_of_int in
  let w = 700. in
  (foi y /. w *. scale -. 0.5),
  (foi x /. w *. scale -. 0.5),
  (foi z /. w -. 0.5)

(* Functions *)

let init () =
  GL.glClearColor 0.467 0.71 1.0 1.0

let getmode () = match !mode with
    Wire -> GL.GL_LINE
  | Fill -> GL.GL_FILL
  | Point -> GL.GL_POINT

let set_objs o = objs := o
let set_heights h = heights := h

let display () =
  let foi x = float_of_int x /. 255. in
  let get_color h =
    let rec sub = function
      | [] -> (0.0, 0.0, 0.0)
      | (h_, (r,g,b))::_ when h_ = h -> (foi r, foi g, foi b)
      | _::xs -> sub xs
    in sub !heights in
  let draw obj =
    let draw_triangle tr =
      GL.glBegin GL.GL_TRIANGLES;
      let aa,ab,ac = fit (tr#s1#get_x,tr#s1#get_y,tr#s1#get_z)
      and acr,acg,acb = get_color tr#s1#get_z
      and ba,bb,bc = fit (tr#s2#get_x,tr#s2#get_y,tr#s2#get_z)
      and bcr,bcg,bcb = get_color tr#s2#get_z
      and ca,cb,cc = fit (tr#s3#get_x,tr#s3#get_y,tr#s3#get_z)
      and ccr,ccg,ccb = get_color tr#s3#get_z in
      GL.glColor3 ~r:acr ~g:acg ~b:acb;
      GL.glVertex3 aa ab ac;
      GL.glColor3 ~r:bcr ~g:bcg ~b:bcb;
      GL.glVertex3 ba bb bc;
      GL.glColor3 ~r:ccr ~g:ccg ~b:ccb;
      GL.glVertex3 ca cb cc;
      GL.glEnd();
    in List.iter draw_triangle obj in
  GL.glClear [GL.GL_COLOR_BUFFER_BIT];
  GL.glLoadIdentity ();
  GL.glRotate ~angle:(float(- !angley)) ~x:1.0 ~y:0.0 ~z:0.0;
  GL.glRotate ~angle:(float(- !anglex)) ~x:0.0 ~y:0.0 ~z:1.0;
  Glu.gluPerspective ~fovy:0.50 ~aspect:1.0 ~zNear:50.0 ~zFar:50.0;
  GL.glPolygonMode GL.GL_FRONT_AND_BACK (getmode ());
  List.iter draw !objs;
  GL.glFlush ();
  Glut.glutSwapBuffers ()

(* active mouse motion *)
let motion ~x ~y =
  if !b_down then  (* if the left button is down *)
    begin
      (* change the rotation angles according to the last position
	 of the mouse and the new one *)
      anglex := !anglex + (!xold - x);
      angley := !angley + (!yold - y);
      Glut.glutPostRedisplay();
    end;
  xold := x;  (* save mouse position *)
  yold := y;
;;

(* mouse button event *)
let mouse ~button ~state ~x ~y =
  match button, state with
    (* if we press the left button *)
    | Glut.GLUT_LEFT_BUTTON, Glut.GLUT_DOWN ->
      b_down := true;
      xold := x;  (* save mouse position *)
      yold := y;
    | Glut.GLUT_LEFT_BUTTON, Glut.GLUT_UP -> b_down := false
    | _ -> ()

let keyboard ~key ~x ~y = match key with
  | '\027' -> begin
    Hashtbl.iter (fun _ a -> Image_functions.remove_tmp a) Image.images; 
    exit 0 end
  | 'w' -> mode := Wire
  | 'f' -> mode := Fill
  | 'p' -> mode := Point
  | 'h' ->
    objs := List.fold_left (fun acc x -> List.rev x :: acc) [] !objs
  | 'F' -> Glut.glutFullScreen ()
  | _ -> ()

let main () =
  started := true;
  Glut.glutInit Sys.argv |> ignore;
  Glut.glutInitDisplayMode [Glut.GLUT_DOUBLE];
  Glut.glutCreateWindow ~title:"kushou's test window" |> ignore;
  init ();
  Glut.glutDisplayFunc ~display;
  Glut.glutKeyboardFunc ~keyboard;
  Glut.glutMouseFunc ~mouse;
  Glut.glutMotionFunc ~motion;
  Glut.glutIdleFunc ~idle:(Glut.glutPostRedisplay);
  Glut.glutMainLoop()

